﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Cofe.Core;
using Cofe.Core.Entry;
using Cofe.Core.Filter;
using Cofe.Core.Interfaces;
using Cofe.Core.Property;
using Cofe.Core.Service;

namespace Cofe.Data
{
    public class RepositoryListActionProvider : ListActionProvider
    {
        #region Factory
        [Export(typeof(ICofeService))]
        [ServicePriority(ServicePriorityAttribute.DefaultPriority_COFEDB)]
        public new class Factory : ISecondaryPropertyProviderFactory, INotifyImported
        {
            public IEnumerable<IPropertyProvider> GetSecoondaryPropertyProviders(IPropertyHost propertyHost)
            {
                bool isDirectory = propertyHost.Behaviors.IsPropertySupported(CofeDirectoryProperties.ListCore) ||
                     propertyHost.Behaviors.IsPropertySupported(CofeDirectoryProperties.ListCoreAsync);

                if (!(propertyHost.SecondaryPropertyProviders.Any((p) => p is IListActionProvider)) && isDirectory)
                    yield return new RepositoryListActionProvider(propertyHost);
            }

            public void OnImported(IServiceLocater serviceLocater)
            {
                _repositoryFactory = serviceLocater.TryFindService<IRepositoryFactory>();
            }

            private IRepositoryFactory _repositoryFactory = null;
            private IServiceLocater _serviceLocater = null;

            public IServiceLocater ServiceLocater
            {
                get { return _serviceLocater ?? CofeServices.ServiceLocater; }
                set { _serviceLocater = value; }
            }



        }
        #endregion

        #region Constructor

        public RepositoryListActionProvider(IPropertyHost propertyHost)
            : base(propertyHost, CofeServices.EntryConstructor)
        {

            registerProperties(CofeDirectoryProperties.HasSubFolders, () =>
            {
                using (var repository = RepositoryFactory.CreateRepository())
                {
                    return repository.Exists(FilterCollections.FromFilters(new RootEntryFilter(Entity), 
                        new FileTypeEntryFilter(FileTypeEntryFilter.Folder), 
                        new SearchOptionOptionFilter(SearchOption.TopDirectoryOnly)));
                }
            });

            registerProperties(CofeDirectoryProperties.HasFiles, () =>
            {
                using (var repository = RepositoryFactory.CreateRepository())
                {
                    return repository.Exists(FilterCollections.FromFilters(new RootEntryFilter(Entity),
                        new FileTypeEntryFilter(FileTypeEntryFilter.File),
                        new SearchOptionOptionFilter(SearchOption.TopDirectoryOnly)));
                }
            });
        }

        #endregion

        #region Methods

        protected override Task<IEnumerable<ICofeSystemInfo>> listAsync(ParameterDic parameters)
        {
            bool enableCache = !parameters.Keys.Contains("EnableCache") || (bool)parameters["EnableCache"];
            Console.WriteLine("RepositoryListActionProvider : EnableCache = " + enableCache);
            if (enableCache)
            {
                return Task.FromResult<IEnumerable<ICofeSystemInfo>>(list(parameters));
            }
            return Task.FromResult<IEnumerable<ICofeSystemInfo>>(null);
        }



        protected override IEnumerable<ICofeSystemInfo> list(ParameterDic parameters)
        {
            var cofeAttribute = Entity.Behaviors.GetProperty<CofeAttributes>(CofeProperties.CofeAttributes);
            if (parameters.EnableCache && !cofeAttribute.HasFlag(CofeAttributes.NotCachable)
                && !cofeAttribute.HasFlag(CofeAttributes.ChildNotCachable))
            {

                IPropertyHost eph = Entity;
                IEntryFilter[] entryFilters; IOptionFilter[] optionFilters;
                loadParameters(parameters, out entryFilters, out  optionFilters);
                var paginate = optionFilters.FirstOrDefault(f => f is PaginateOptionFilter) as PaginateOptionFilter;

                entryFilters = entryFilters.Union(
                    new IEntryFilter[] 
                        {
                            new RootEntryFilter(Entity),
                            new PropertyEntryFilter(CofeProperties.Type, EntryTypes.EntryLink) { Negative = true }
                        }).ToArray();


                using (var repository = RepositoryFactory.CreateRepository())
                {
                    DatabaseEntry dbEntry = repository.FindDbEntry(FilterCollections.FromPropertyHost(eph)).FirstOrDefault();
                    var expired = dbEntry == null || dbEntry.ListExpired;
                    if (!expired)
                    {
                        parameters.IsHandled = true;
                        var filters = FilterCollections.FromFilters(entryFilters, optionFilters);

                        if (paginate != null)
                        {
                            var count = repository.Count(filters);

                            parameters.AddOrUpdate(ParameterKeys.CurrentPage, paginate.Page);                            
                            parameters.AddOrUpdate(ParameterKeys.TotalPages, (int)Math.Ceiling( (float)count / paginate.ItemsPerPage));
                            parameters.AddOrUpdate(ParameterKeys.TotalEntries, count);
                            parameters.AddOrUpdate(ParameterKeys.ItemsPerPage, paginate.ItemsPerPage);
                        }

                        //entryFilters = entryFilters.Concat(new IEntryFilter[] { new RootEntryFilter(eph) }).ToArray();
                        return repository.FindAll(filters)
                            .Select(ph => CofeServices.EntryConstructor.ConstructEntry(ph))
                            .ToList();
                    }
                }

                if (!(cofeAttribute.HasFlag(CofeAttributes.NotCachable)))
                    if (CofeServices.CofeSettings.CacheUpdateOptions.HasFlag(CacheUpdateOptions.passiveUpdate))
                        Entity.Behaviors.InvokeAsync(DatabaseProperties.WriteRepositoryAsync,
                                new ParameterDic() { UpdateSubEntries = true });
                return base.list(eph, entryFilters, optionFilters);
            }
            else
                return null;
        }


        #endregion

        #region Data

        private IRepositoryFactory _repositoryFactory = null;

        #endregion

        #region Public Properties

        public IRepositoryFactory RepositoryFactory { get { return _repositoryFactory ?? (_repositoryFactory = CofeServices.ServiceLocater.FindService<IRepositoryFactory>()); } }
        #endregion

    }
}
